﻿<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0" />
    <meta name="author" content="火星科技 http://mars3d.cn " />
    <meta name="apple-touch-fullscreen" content="yes" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="x5-fullscreen" content="true" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
    <!-- 标题及搜索关键字 -->
    <meta name="keywords" content="火星科技,cesium,3D,GIS,marsgis,三维,地球,地图,开发,框架,系统,示例,资料,模型,离线,外包,合肥,安徽,中国" />
    <meta
      name="description"
      content="火星科技 合肥火星 合肥火星科技 合肥火星科技有限公司 leaflet leaflet框架 leaflet开发 cesium cesium开发 cesium框架 三维 地球 模型  gis marsgis 地图离线 地图开发 地图框架 地图外包 框架 开发 外包  地图离线 二维地图 三维地图 全景漫游 地理信息系统 云GIS 三维GIS GIS平台 WebGIS"
    />


    <link rel="shortcut icon" type="image/x-icon" href="http://mars3d.cn/favicon.ico" />
    <title>Mesh可视化扩展  | Mars3D | 三维地图 | 火星科技 | 合肥火星科技有限公司</title>

    <script
      type="text/javascript"
      src="../lib/include-lib.js"
      libpath="../lib/"
      include="jquery,font-awesome,bootstrap,layer,haoutil,turf,mars3d"
    ></script>

    <link href="css/style.css" rel="stylesheet" />
  </head>
  <body class="dark">
    <div id="mars3dContainer" class="mars3d-container"></div>



    <script id="2d-vertex-shader" type="x-shader/x-vertex">
      attribute vec2 a_position;

      void main() {
      gl_Position = vec4(a_position, 0, 1);
      }
    </script>
    <script id="resetVelocityShader" type="x-shader/x-fragment">
      precision mediump float;

      void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 0.0);
      }
    </script>
    <script id="boundaryConditionsShader" type="x-shader/x-fragment">
      precision mediump float;

      uniform sampler2D u_texture;
      uniform float u_scale;
      uniform vec2 u_textureSize;

      uniform vec2 u_obstaclePosition;//scaled to texture size
      uniform float u_obstacleRad;

      void main() {
      vec2 fragCoord = gl_FragCoord.xy;

      vec2 dir = fragCoord - 3.0*vec2(0.5, 0.5) - u_obstaclePosition;//not sure where this fac of 3 came from?
      float dist = length(dir);
      if (dist < u_obstacleRad){
      gl_FragColor = vec4(0);
      return;
      }

      if (dist < u_obstacleRad+1.0){
      gl_FragColor = u_scale*texture2D(u_texture, (fragCoord + dir/dist*2.0)/u_textureSize);
      return;
      }

      gl_FragColor = texture2D(u_texture, fragCoord/u_textureSize);
      }
    </script>
    <script id="2d-render-shader" type="x-shader/x-fragment">
      precision mediump float;

      uniform sampler2D u_material;
      uniform vec2 u_obstaclePosition;
      uniform vec2 u_textureSize;
      uniform float u_obstacleRad;

      void main() {
      vec2 fragCoord = gl_FragCoord.xy;

      vec2 dir = fragCoord - vec2(0.5, 0.5) - u_obstaclePosition;
      float dist = length(dir);
      if (dist < u_obstacleRad){
      gl_FragColor = vec4(0.925, 0, 0.55, 1);
      return;
      }

      float mat1 = texture2D(u_material, fragCoord/u_textureSize).x;
      vec3 color = vec3(0.98, 0.93, 0.84);
      gl_FragColor = vec4(mat1*color, 1);
      }
    </script>
    <script id="gradientSubtractionShader" type="x-shader/x-fragment">
      precision mediump float;

      uniform sampler2D u_velocity;
      uniform sampler2D u_pressure;

      uniform vec2 u_textureSize;

      uniform float u_const;

      vec2 repeatBoundary(vec2 coord){
      coord -= vec2(0.5, 0.5);
      //if (coord.x < 0.0) coord.x = u_textureSize.x-1.0;
      if (coord.x >= u_textureSize.x-1.0) coord.x = 0.0;
      if (coord.y < 0.0) coord.y = u_textureSize.y-1.0;
      else if (coord.y >= u_textureSize.y-1.0) coord.y = 0.0;
      coord += vec2(0.5, 0.5);
      return coord;
      }

      void main() {

      vec2 fragCoord = gl_FragCoord.xy;

      vec2 currentVelocity = texture2D(u_velocity, fragCoord/u_textureSize).xy;

      float n = texture2D(u_pressure, repeatBoundary(fragCoord+vec2(0.0, 1.0))/u_textureSize).x;
      float s = texture2D(u_pressure, repeatBoundary(fragCoord+vec2(0.0, -1.0))/u_textureSize).x;
      float e = texture2D(u_pressure, (fragCoord+vec2(1.0, 0.0))/u_textureSize).x;
      float w = texture2D(u_pressure, (fragCoord+vec2(-1.0, 0.0))/u_textureSize).x;

      gl_FragColor = vec4(currentVelocity-u_const*vec2(e-w, n-s), 0, 0);
      }
    </script>
    <script id="divergenceShader" type="x-shader/x-fragment">
      precision mediump float;

      uniform sampler2D u_velocity;

      uniform vec2 u_textureSize;

      uniform float u_const;

      vec2 repeatBoundary(vec2 coord){
      coord -= vec2(0.5, 0.5);
      if (coord.x < 0.0) coord.x = u_textureSize.x-1.0;
      else if (coord.x >= u_textureSize.x-1.0) coord.x = 0.0;
      if (coord.y < 0.0) coord.y = u_textureSize.y-1.0;
      else if (coord.y >= u_textureSize.y-1.0) coord.y = 0.0;
      coord += vec2(0.5, 0.5);
      return coord;
      }

      void main() {

      vec2 fragCoord = gl_FragCoord.xy;

      //finite difference formulation of divergence

      //periodic boundary

      float n = texture2D(u_velocity, repeatBoundary(fragCoord+vec2(0.0, 1.0))/u_textureSize).y;
      float s = texture2D(u_velocity, repeatBoundary(fragCoord+vec2(0.0, -1.0))/u_textureSize).y;

      float e = texture2D(u_velocity, repeatBoundary(fragCoord+vec2(1.0, 0.0))/u_textureSize).x;
      float w = texture2D(u_velocity, (fragCoord+vec2(-1.0, 0.0))/u_textureSize).x;

      float div = u_const*(e-w + n-s);
      gl_FragColor = vec4(div, 0, 0, 0);
      }
    </script>
    <script id="forceShader" type="x-shader/x-fragment">
      precision mediump float;

      uniform sampler2D u_velocity;

      uniform vec2 u_textureSize;

      uniform vec2 u_mouseCoord;
      uniform vec2 u_mouseDir;

      uniform float u_reciprocalRadius;

      uniform float u_dt;

      void main() {

      vec2 fragCoord = gl_FragCoord.xy;

      vec2 currentVelocity = texture2D(u_velocity, fragCoord/u_textureSize).xy;

      vec2 pxDist = fragCoord - u_mouseCoord;
      currentVelocity += u_mouseDir*u_dt*exp(-(pxDist.x*pxDist.x+pxDist.y*pxDist.y)*u_reciprocalRadius);

      gl_FragColor = vec4(currentVelocity, 0, 0);
      }
    </script>
    <script id="jacobiShader" type="x-shader/x-fragment">
      precision mediump float;

      uniform sampler2D u_b;
      uniform sampler2D u_x;

      uniform vec2 u_textureSize;

      uniform float u_alpha;
      uniform float u_reciprocalBeta;

      void main() {

      vec2 fragCoord = gl_FragCoord.xy;

      vec2 currentState = texture2D(u_b, fragCoord/u_textureSize).xy;

      //implicitly solve diffusion via jacobi iteration

      vec2 n = texture2D(u_x, (fragCoord+vec2(0.0, 1.0))/u_textureSize).xy;
      vec2 s = texture2D(u_x, (fragCoord+vec2(0.0, -1.0))/u_textureSize).xy;
      vec2 e = texture2D(u_x, (fragCoord+vec2(1.0, 0.0))/u_textureSize).xy;
      vec2 w = texture2D(u_x, (fragCoord+vec2(-1.0, 0.0))/u_textureSize).xy;

      vec2 nextState = (n + s + e + w + u_alpha * currentState) * u_reciprocalBeta;

      gl_FragColor = vec4(nextState, 0, 0);
      }
    </script>
    <script id="advectShaderMat" type="x-shader/x-fragment">
      precision mediump float;

      uniform sampler2D u_velocity;
      uniform sampler2D u_material;

      uniform vec2 u_textureSize;
      uniform float u_scale;

      uniform float u_dt;

      vec2 bilinearInterp(vec2 pos, sampler2D texture, vec2 size){
      //bilinear interp between nearest cells

      vec2 pxCenter = vec2(0.5, 0.5);

      vec2 ceiled = ceil(pos);
      vec2 floored = floor(pos);

      vec2 n = texture2D(texture, (ceiled+pxCenter)/size).xy;//actually ne
      vec2 s = texture2D(texture, (floored+pxCenter)/size).xy;//actually sw
      if (ceiled.x != floored.x){
      vec2 se = texture2D(texture, (vec2(ceiled.x, floored.y)+pxCenter)/size).xy;
      vec2 nw = texture2D(texture, (vec2(floored.x, ceiled.y)+pxCenter)/size).xy;
      n = n*(pos.x-floored.x) + nw*(ceiled.x-pos.x);
      s = se*(pos.x-floored.x) + s*(ceiled.x-pos.x);
      }
      vec2 materialVal = n;
      if (ceiled.y != floored.y){
      materialVal = n*(pos.y-floored.y) + s*(ceiled.y-pos.y);
      }
      return materialVal;
      }

      void main() {

      vec2 fragCoord = gl_FragCoord.xy;

      vec2 pxCenter = vec2(0.5, 0.5);

      //bilinear interp
      //vec2 currentVelocity = 1.0/u_scale*texture2D(u_velocity, fragCoord/u_textureSize).xy;
      vec2 currentVelocity = 1.0/u_scale*bilinearInterp((fragCoord-pxCenter)*u_scale + pxCenter, u_velocity, u_textureSize*u_scale);

      //implicitly solve advection

      if (fragCoord.x < 1.0){//boundary
      float numCols = floor(u_textureSize.y/10.0);
      if (mod(numCols, 2.0) == 1.0) numCols--;
      float numPx = u_textureSize.y/numCols;
      if (floor(mod((fragCoord.y-2.0)/numPx, 2.0)) == 0.0) gl_FragColor = vec4(1, 0, 0, 0);
      else gl_FragColor = vec4(0, 0, 0, 0);
      return;
      }
      if (length(currentVelocity) == 0.0) {//no velocity
      gl_FragColor = vec4(texture2D(u_material, fragCoord/u_textureSize).xy, 0, 0);
      return;
      }

      vec2 pos = fragCoord - pxCenter - u_dt*currentVelocity;

      if (pos.x >= u_textureSize.x-1.0) {
      gl_FragColor = vec4(0, 0, 0, 0);
      return;
      }

      //periodic boundary in y
      if (pos.y < 0.0) pos.y += u_textureSize.y-1.0;
      if (pos.y >= u_textureSize.y-1.0) pos.y -= u_textureSize.y-1.0;

      gl_FragColor = vec4(bilinearInterp(pos, u_material, u_textureSize), 0, 0);
      }
    </script>
    <script id="advectShaderVel" type="x-shader/x-fragment">
      precision mediump float;

      uniform sampler2D u_velocity;
      uniform sampler2D u_material;

      uniform vec2 u_textureSize;
      uniform float u_scale;

      uniform float u_dt;

      void main() {

      vec2 fragCoord = gl_FragCoord.xy;

      vec2 currentVelocity = u_scale*texture2D(u_velocity, fragCoord/u_textureSize).xy;

      //implicitly solve advection

      if (length(currentVelocity) == 0.0) {//boundary or no velocity
      gl_FragColor = vec4(texture2D(u_material, fragCoord/u_textureSize).xy, 0, 0);
      return;
      }

      vec2 pxCenter = vec2(0.5, 0.5);
      vec2 pos = fragCoord - pxCenter - u_dt*currentVelocity;

      if (pos.x < 1.0) {
      gl_FragColor = vec4(1.0, 0, 0, 0);
      return;
      }
      if (pos.x >= u_textureSize.x-1.0) {
      gl_FragColor = vec4(0, 0, 0, 0);
      //return;
      }
      if (pos.x >= u_textureSize.x-1.0) pos.x -= u_textureSize.x-1.0;

      //periodic boundary in y
      if (pos.y < 0.0) {
      pos.y += u_textureSize.y-1.0;
      }
      if (pos.y >= u_textureSize.y-1.0) {
      pos.y -= u_textureSize.y-1.0;
      }

      //bilinear interp between nearest cells
      vec2 ceiled = ceil(pos);
      vec2 floored = floor(pos);

      vec2 n = texture2D(u_material, (ceiled+pxCenter)/u_textureSize).xy;//actually ne
      vec2 s = texture2D(u_material, (floored+pxCenter)/u_textureSize).xy;//actually sw
      if (ceiled.x != floored.x){
      vec2 se = texture2D(u_material, (vec2(ceiled.x, floored.y)+pxCenter)/u_textureSize).xy;
      vec2 nw = texture2D(u_material, (vec2(floored.x, ceiled.y)+pxCenter)/u_textureSize).xy;
      n = n*(pos.x-floored.x) + nw*(ceiled.x-pos.x);
      s = se*(pos.x-floored.x) + s*(ceiled.x-pos.x);
      }
      vec2 materialVal = n;
      if (ceiled.y != floored.y){
      materialVal = n*(pos.y-floored.y) + s*(ceiled.y-pos.y);
      }

      gl_FragColor = vec4(materialVal, 0, 0);
      }
    </script>

    <script src="../lib/mars3d/thirdParty/meshVisualizer/CesiumMeshVisualizer.js"></script>

    <script src="./js/common.js"></script>
    <script type="text/javascript">
      "use script"; //开发环境建议开启严格模式

      var map;

      var meshVisualizer;
      var shaders;
      let MeshVisualizer = Cesium.MeshVisualizer;
      let Mesh = Cesium.Mesh;
      let MeshMaterial = Cesium.MeshMaterial;
      let FramebufferTexture = Cesium.FramebufferTexture;
      let GeometryUtils = Cesium.GeometryUtils;
      let LOD = Cesium.LOD;

      function initMap(options) {
        //合并属性参数，可覆盖config.json中的对应配置
        var mapOptions = mars3d.Util.merge(options, {
          scene: {
            center: { lat: 31.582443, lng: 117.236137, alt: 31861, heading: 1, pitch: -47 },
            fxaa: true,
          },
        });

        //创建三维地球场景
        map = new mars3d.Map("mars3dContainer", mapOptions);

        var gl = map.scene.frameState.context._gl;

        var center = Cesium.Cartesian3.fromDegrees(117.220206, 31.834866, 50);
        var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);

        meshVisualizer = new MeshVisualizer({
          modelMatrix: modelMatrix,
          up: { z: 1 },
        });
        map.scene.globe.show = true;
        map.scene.primitives.add(meshVisualizer);
        // meshVisualizer.showReference = true; //显示坐标轴

        shaders = {
          "2d-render-shader": document.getElementById("2d-render-shader").text,
          "2d-vertex-shader": document.getElementById("2d-vertex-shader").text,
          advectShaderMat: document.getElementById("advectShaderMat").text,
          advectShaderVel: document.getElementById("advectShaderVel").text,
          boundaryConditionsShader: document.getElementById("boundaryConditionsShader").text,
          divergenceShader: document.getElementById("divergenceShader").text,
          forceShader: document.getElementById("forceShader").text,
          gradientSubtractionShader: document.getElementById("gradientSubtractionShader").text,
          jacobiShader: document.getElementById("jacobiShader").text,
          resetVelocityShader: document.getElementById("resetVelocityShader").text,
        };

        requestAnimationFrame(function () {
          try {
            main();
          } catch (e) {
            alert(e.message);
          }
        });
      }

      function main() {
        var dt = 1;
        var dx = 1;
        var nu = 0.0000003; //viscosity
        var rho = 0.21; //density
        var actualWidth = map.scene.canvas.clientWidth; //document.body.clientWidth;
        var actualHeight = map.scene.canvas.clientHeight; // document.body.clientHeight;
        var obstaclePosition = [0, 0];
        var obstacleRad = 84; //球的直径
        var movingObstacle = true;

        var lastMouseCoordinates = [0, 0];
        var mouseCoordinates = [0, 0];
        var mouseEnable = false;

        var paused = false; //while window is resizing

        var gridSize = 200;
        var maxDim = Math.max(actualHeight, actualWidth);
        var scale = maxDim / gridSize;
        var alpha = (dx * dx) / (nu * dt);
        let width = Math.floor(actualWidth / scale);
        let height = Math.floor(actualHeight / scale);

        var gl = map.scene.frameState.context._gl;
        var ext = gl.getExtension("OES_texture_half_float") || gl.getExtension("EXT_color_buffer_half_float");

        function createEmptyTexture(frameState, width, height, id) {
          var emptyTexture = new Cesium.Texture({
            context: frameState.context,
            source: {
              width: width,
              height: height,
              arrayBufferView: ext ? new Float32Array(width * height * 4) : new Uint8Array(width * height * 4),
            },
            target: Cesium.WebGLConstants.TEXTURE_2D,
            width: width,
            height: height,
            pixelDatatype: ext ? Cesium.PixelDatatype.FLOAT : void 0,
            sampler: new Cesium.Sampler({
              minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
              magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            }),
          });
          emptyTexture.id = id;
          return emptyTexture;
        }

        var textures = {
          velocity: createEmptyTexture(map.scene.frameState, width, height, "velocity"),
          nextVelocity: createEmptyTexture(map.scene.frameState, width, height, "nextVelocity"),
          material: createEmptyTexture(map.scene.frameState, actualWidth, actualWidth, "material"),
          nextMaterial: createEmptyTexture(map.scene.frameState, actualWidth, actualWidth, "nextMaterial"),
          pressure: createEmptyTexture(map.scene.frameState, width, height, "pressure"),
          nextPressure: createEmptyTexture(map.scene.frameState, width, height, "nextPressure"),
          u_b: createEmptyTexture(map.scene.frameState, width, height, "u_b"),
          u_x: createEmptyTexture(map.scene.frameState, width, height, "u_x"),
          u_texture: createEmptyTexture(map.scene.frameState, width, height, "u_texture"),
          velocityDivergence: createEmptyTexture(map.scene.frameState, width, height, "velocityDivergence"),
        };
        var geometry = new Cesium.PlaneBufferGeometry(2, 2);

        var advectVelMtl = new MeshMaterial({
          blending: false,
          uniforms: {
            u_dt: dt,
            u_velocity: textures["velocity"], //速度纹理
            u_material: textures["material"], //
            u_scale: -1,
            u_textureSize: new Cesium.Cartesian2(width, height),
          },
          vertexShader: shaders["2d-vertex-shader"], //shaders["2d-vertex-shader"],
          fragmentShader: shaders["advectShaderVel"], // document.getElementById("advectShaderVel").textContent
        });

        var fb_advectVel = new FramebufferTexture(new Mesh(geometry, advectVelMtl));

        var advectMatMtl = new MeshMaterial({
          blending: false,
          uniforms: {
            u_dt: dt,
            u_velocity: textures["velocity"], //速度纹理
            u_material: textures["material"], //
            u_scale: width / actualWidth,
            u_textureSize: new Cesium.Cartesian2(actualWidth, actualHeight),
          },
          vertexShader: shaders["2d-vertex-shader"],
          fragmentShader: shaders["advectShaderMat"], //document.getElementById("advectShaderMat").textContent
        });
        var fb_advectMat = new FramebufferTexture(new Mesh(geometry, advectMatMtl));

        var gradientSubtractionMtl = new MeshMaterial({
          blending: false,
          uniforms: {
            u_const: 0.5 / dx,
            u_velocity: textures["velocity"], //速度纹理
            u_pressure: textures["pressure"], //
            u_textureSize: new Cesium.Cartesian2(width, height),
          },
          vertexShader: shaders["2d-vertex-shader"],
          fragmentShader: shaders["gradientSubtractionShader"], //document.getElementById("gradientSubtractionShader").textContent
        });
        var fb_gradientSubtraction = new FramebufferTexture(new Mesh(geometry, gradientSubtractionMtl));

        var divergeMtl = new MeshMaterial({
          blending: false,
          uniforms: {
            u_const: 0.5 / dx,
            u_velocity: textures["velocity"], //速度纹理
            u_textureSize: new Cesium.Cartesian2(width, height),
          },
          vertexShader: shaders["2d-vertex-shader"],
          fragmentShader: shaders["divergenceShader"], //document.getElementById("divergenceShader").textContent
        });
        var fb_diverge = new FramebufferTexture(new Mesh(geometry, divergeMtl));

        var forceMtl = new MeshMaterial({
          blending: false,
          uniforms: {
            u_dt: dt,
            u_velocity: textures["velocity"], //速度纹理
            u_textureSize: new Cesium.Cartesian2(width, height),
            u_mouseCoord: new Cesium.Cartesian2(width / 2, height / 2),
            u_mouseDir: new Cesium.Cartesian2(width / 2, height / 2),
            u_reciprocalRadius: 0.1 * scale,
          },
          vertexShader: shaders["2d-vertex-shader"],
          fragmentShader: shaders["forceShader"], //document.getElementById("forceShader").textContent
        });
        var fb_force = new FramebufferTexture(new Mesh(geometry, forceMtl));

        var jacobiMtl = new MeshMaterial({
          blending: false,
          uniforms: {
            u_b: textures["u_b"],
            u_x: textures["u_x"],
            u_alpha: alpha,
            u_reciprocalBeta: 1 / (4 + alpha),
            u_textureSize: new Cesium.Cartesian2(width, height),
          },
          vertexShader: shaders["2d-vertex-shader"],
          fragmentShader: shaders["jacobiShader"], //document.getElementById("jacobiShader").textContent
        });
        var fb_jacobi = new FramebufferTexture(new Mesh(geometry, jacobiMtl));

        var renderMtl = new MeshMaterial({
          blending: false,
          uniforms: {
            u_obstacleRad: obstacleRad,
            u_material: textures["material"],
            u_obstaclePosition: new Cesium.Cartesian2((obstaclePosition[0] * width) / actualWidth, (obstaclePosition[1] * height) / actualHeight),
            u_textureSize: new Cesium.Cartesian2(actualWidth, actualHeight),
          },
          vertexShader: shaders["2d-vertex-shader"],
          fragmentShader: shaders["2d-render-shader"], // document.getElementById("2d-render-shader").textContent
        });

        var boundaryMtl = new MeshMaterial({
          blending: false,
          uniforms: {
            u_obstacleRad: obstacleRad,
            u_texture: textures["nextVelocity"],
            u_obstaclePosition: new Cesium.Cartesian2((obstaclePosition[0] * width) / actualWidth, (obstaclePosition[1] * height) / actualHeight),
            u_textureSize: new Cesium.Cartesian2(width, height),
            u_scale: -1,
          },
          vertexShader: shaders["2d-vertex-shader"],
          fragmentShader: shaders["boundaryConditionsShader"], //document.getElementById("boundaryConditionsShader").textContent
        });
        var fb_boundary = new FramebufferTexture(new Mesh(geometry, boundaryMtl));

        var resetVelocityMtl = new MeshMaterial({
          blending: false,
          vertexShader: shaders["2d-vertex-shader"],
          fragmentShader: shaders["resetVelocityShader"], //document.getElementById("resetVelocityShader").textContent
        });
        var fb_resetVelocity = new FramebufferTexture(new Mesh(geometry, resetVelocityMtl));

        // meshVisualizer.add(new Mesh(geometry, renderMtl));
        var west = -10000,
          south = -5000,
          east = 10000,
          north = 5000;
        height = 0;
        function createGeometry(west, south, east, north, height) {
          (west = Cesium.defaultValue(west, -10000)),
            (south = Cesium.defaultValue(south, -5000)),
            (east = Cesium.defaultValue(east, 10000)),
            (north = Cesium.defaultValue(north, 5000));
          height = Cesium.defaultValue(height, 0);
          var p1 = new Cesium.Cartesian3(west, north, height);
          var p2 = new Cesium.Cartesian3(west, south, height);
          var p3 = new Cesium.Cartesian3(east, south, height);
          var p4 = new Cesium.Cartesian3(east, north, height);

          var positions = new Float64Array([p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, p4.x, p4.y, p4.z]);
          var indices = new Uint16Array([0, 1, 3, 1, 2, 3]);
          var sts = new Float32Array([0, 0, 0, 1, 1, 1, 1, 0]);
          var geometry = new Cesium.Geometry({
            attributes: {
              position: new Cesium.GeometryAttribute({
                componentDatatype: Cesium.ComponentDatatype.DOUBLE,
                componentsPerAttribute: 3,
                values: positions,
              }),
              st: new Cesium.GeometryAttribute({
                componentDatatype: Cesium.ComponentDatatype.FLOAT,
                componentsPerAttribute: 2,
                values: sts,
              }),
            },
            indices: indices,
            primitiveType: Cesium.PrimitiveType.TRIANGLES,
            boundingSphere: Cesium.BoundingSphere.fromVertices(positions),
          });

          return geometry;
        }
        var customMeshMtl = new MeshMaterial({
          uniforms: {
            u_material: textures.velocity,
            u_textureSize: new Cesium.Cartesian2(actualWidth, actualHeight),
            u_obstacleRad: obstacleRad,
            u_obstaclePosition: new Cesium.Cartesian2(obstaclePosition[0], obstaclePosition[1]),
          },
          side: MeshMaterial.Sides.DOUBLE,
          vertexShader: `varying vec3 v_position;
                varying vec2 v_st;

                void main(void)
                {
                vec4 pos = u_modelViewMatrix * vec4(position,1.0);
                v_position = pos.xyz;
                v_st=st;
                gl_Position = u_projectionMatrix * pos;
                }`,
          fragmentShader: `
              precision mediump float;
              varying vec2 v_st;
              uniform sampler2D u_material; //值为0-1的一个灰度映射  存放在  r 通道  lsh
              uniform vec2 u_obstaclePosition;
              uniform vec2 u_textureSize;
              uniform float u_obstacleRad;

              void main() {
                vec2 fragCoord = u_textureSize*v_st;//gl_FragCoord.xy;

                vec2 dir = fragCoord - vec2(0.5, 0.5) - u_obstaclePosition;
                float dist = length(dir);
                if (dist < u_obstacleRad){
                    gl_FragColor = vec4(0.0, 0, 0.0, 1);  //圆的颜色 lsh
                    return;
                }

                float mat1 = texture2D(u_material, fragCoord/u_textureSize).x;
                vec3 color = vec3(0.98, 0.93, 0.84);
                //https://www.shadertoy.com/view/MsS3Wc    hsv
                //vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );
                vec3 rgb = clamp( abs(mod((mat1*0.9+0.5)*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );//lsh
                // vec3 rgb = mix(vec3(1.0,1.0,1.0),vec3(0.0,0.0,1.0),mat1*color);//lsh
                //vec3 rgb = mat1*color;
                gl_FragColor =  vec4(rgb, 1);
              }`,
        });
        var customMesh = new Mesh(createGeometry(west, south, east, north, height), customMeshMtl);
        meshVisualizer.add(customMesh);

        function swap(object, texture1Name, texture2Name) {
          var temp = object[texture1Name];
          object[texture1Name] = object[texture2Name];
          object[texture2Name] = temp;
        }
        var hasStart = false;
        paused = false;

        var viewport = { x: 0, y: 0, width: actualWidth, height: actualHeight };
        function setSize(w, h) {
          viewport.width = w;
          viewport.height = h;
        }

        resetWindow();
        var lastTime = new Date();
        var deltTime = 0;

        meshVisualizer.beforeUpdate.addEventListener(function (frameState) {
          if (paused) {
            return;
          }
          deltTime = new Date() - lastTime;
          if (deltTime > 1000) {
            console.log("frame time:" + deltTime + "ms");
          }

          lastTime = new Date();
          if (hasStart) {
            swap(textures, "nextMaterial", "material");
          } else {
            hasStart = true;
          }

          setSize(width, height);
          //advect velocity
          advectVelMtl.uniforms.u_material.value = textures.velocity;
          advectVelMtl.uniforms.u_velocity.value = textures.velocity;
          fb_advectVel.texture = textures.nextVelocity;
          meshVisualizer.updateFrameBufferTexture(frameState, fb_advectVel, viewport);

          if (movingObstacle) {
            (boundaryMtl.uniforms.u_obstaclePosition.value.x = (obstaclePosition[0] * width) / actualWidth),
              (boundaryMtl.uniforms.u_obstaclePosition.value.y = (obstaclePosition[1] * height) / actualHeight);
          }
          boundaryMtl.uniforms.u_texture.value = textures.nextVelocity;
          boundaryMtl.uniforms.u_scale.value = -1;
          fb_boundary.texture = textures.velocity;
          meshVisualizer.updateFrameBufferTexture(frameState, fb_boundary, viewport);

          // diffuse velocity
          var alpha = (dx * dx) / (nu * dt);
          jacobiMtl.uniforms.u_alpha.value = alpha;
          jacobiMtl.uniforms.u_reciprocalBeta.value = 1 / (4 + alpha);
          for (let i = 0; i < 40; i++) {
            jacobiMtl.uniforms.u_b.value = textures.velocity;
            jacobiMtl.uniforms.u_x.value = textures.velocity;
            fb_jacobi.texture = textures.nextVelocity;
            meshVisualizer.updateFrameBufferTexture(frameState, fb_jacobi, viewport);

            jacobiMtl.uniforms.u_b.value = textures.nextVelocity;
            jacobiMtl.uniforms.u_x.value = textures.nextVelocity;
            fb_jacobi.texture = textures.velocity;
            meshVisualizer.updateFrameBufferTexture(frameState, fb_jacobi, viewport);
          }

          //apply force
          if (mouseEnable) {
            (forceMtl.uniforms.u_mouseCoord.value.x = (mouseCoordinates[0] * width) / actualWidth),
              (forceMtl.uniforms.u_mouseCoord.value.y = (mouseCoordinates[1] * height) / actualHeight);
            (forceMtl.uniforms.u_mouseDir.value.x = (2 * (mouseCoordinates[0] - lastMouseCoordinates[0])) / scale),
              (forceMtl.uniforms.u_mouseDir.value.x = (2 * (mouseCoordinates[1] - lastMouseCoordinates[1])) / scale);
            forceMtl.uniforms.u_velocity.value = textures.velocity;
            fb_force.texture = textures.nextVelocity;
            meshVisualizer.updateFrameBufferTexture(frameState, fb_force, viewport);

            boundaryMtl.uniforms.u_scale.value = -1;
            boundaryMtl.uniforms.u_texture.value = textures.nextVelocity;
            fb_boundary.texture = textures.velocity;
            meshVisualizer.updateFrameBufferTexture(frameState, fb_boundary, viewport);
          }

          // compute pressure

          divergeMtl.uniforms.u_velocity.value = textures.velocity;
          fb_diverge.texture = textures.velocityDivergence;
          meshVisualizer.updateFrameBufferTexture(frameState, fb_diverge, viewport); //calc velocity divergence

          jacobiMtl.uniforms.u_alpha.value = -dx * dx;
          jacobiMtl.uniforms.u_reciprocalBeta.value = 1 / 4;
          for (var i = 0; i < 20; i++) {
            jacobiMtl.uniforms.u_b.value = textures.velocityDivergence;
            jacobiMtl.uniforms.u_x.value = textures.pressure;
            fb_jacobi.texture = textures.nextPressure;
            meshVisualizer.updateFrameBufferTexture(frameState, fb_jacobi, viewport); //diffuse velocity

            jacobiMtl.uniforms.u_b.value = textures.velocityDivergence;
            jacobiMtl.uniforms.u_x.value = textures.nextPressure;
            fb_jacobi.texture = textures.pressure;
            meshVisualizer.updateFrameBufferTexture(frameState, fb_jacobi, viewport); //diffuse velocity
          }

          boundaryMtl.uniforms.u_scale.value = 1;
          boundaryMtl.uniforms.u_texture.value = textures.pressure;
          fb_boundary.texture = textures.nextPressure;
          meshVisualizer.updateFrameBufferTexture(frameState, fb_boundary, viewport);
          swap(textures, "nextPressure", "pressure");

          // subtract pressure gradient
          gradientSubtractionMtl.uniforms.u_pressure.value = textures.pressure;
          gradientSubtractionMtl.uniforms.u_velocity.value = textures.velocity;
          fb_gradientSubtraction.texture = textures.nextVelocity;
          meshVisualizer.updateFrameBufferTexture(frameState, fb_gradientSubtraction, viewport);

          boundaryMtl.uniforms.u_scale.value = -1;
          boundaryMtl.uniforms.u_texture.value = textures.nextVelocity;
          fb_boundary.texture = textures.velocity;
          meshVisualizer.updateFrameBufferTexture(frameState, fb_boundary, viewport);

          setSize(actualWidth, actualHeight);
          // move material
          advectMatMtl.uniforms.u_velocity.value = textures.velocity;
          advectMatMtl.uniforms.u_material.value = textures.material;
          fb_advectMat.texture = textures.nextMaterial;
          meshVisualizer.updateFrameBufferTexture(frameState, fb_advectMat, viewport);

          if (movingObstacle) {
            (renderMtl.uniforms.u_obstaclePosition.value.x = obstaclePosition[0]), (renderMtl.uniforms.u_obstaclePosition.value.y = obstaclePosition[1]);
            (customMeshMtl.uniforms.u_obstaclePosition.value.x = obstaclePosition[0]),
              (customMeshMtl.uniforms.u_obstaclePosition.value.y = obstaclePosition[1]);
          }
          renderMtl.uniforms.u_material.value = textures.velocity;
          customMeshMtl.uniforms.u_material.value = textures.velocity;
          deltTime = new Date() - lastTime;
          if (deltTime > 100) {
            console.log("framebuffer time:" + deltTime + "ms");
          }
        });

        function resetWindow() {
          actualWidth = map.scene.canvas.clientWidth; //document.body.clientWidth;
          actualHeight = map.scene.canvas.clientHeight; // document.body.clientHeight;

          var maxDim = Math.max(actualHeight, actualWidth);
          var scale = maxDim / gridSize;

          width = Math.floor(actualWidth / scale);
          height = Math.floor(actualHeight / scale);

          obstaclePosition = [actualWidth / 10, actualHeight / 2]; //初始位置

          advectVelMtl.uniforms.u_textureSize.value = new Cesium.Cartesian2(width, height);
          advectVelMtl.uniforms.u_scale.value = 1;

          advectMatMtl.uniforms.u_textureSize.value = new Cesium.Cartesian2(actualWidth, actualHeight);
          advectMatMtl.uniforms.u_scale.value = width / actualWidth;

          gradientSubtractionMtl.uniforms.u_textureSize.value = new Cesium.Cartesian2(width, height);

          divergeMtl.uniforms.u_textureSize.value = new Cesium.Cartesian2(width, height);

          forceMtl.uniforms.u_reciprocalRadius.value = 0.1 * scale;
          forceMtl.uniforms.u_textureSize.value = new Cesium.Cartesian2(width, height);

          jacobiMtl.uniforms.u_textureSize.value = new Cesium.Cartesian2(width, height);

          (renderMtl.uniforms.u_obstaclePosition.value.x = obstaclePosition[0]), (renderMtl.uniforms.u_obstaclePosition.value.y = obstaclePosition[1]);
          renderMtl.uniforms.u_textureSize.value = new Cesium.Cartesian2(actualWidth, actualHeight);
          renderMtl.uniforms.u_obstacleRad.value = obstacleRad;

          boundaryMtl.uniforms.u_textureSize.value = new Cesium.Cartesian2(width, height);
          boundaryMtl.uniforms.u_obstaclePosition.value = new Cesium.Cartesian2(
            (obstaclePosition[0] * width) / actualWidth,
            (obstaclePosition[1] * height) / actualHeight
          );
          boundaryMtl.uniforms.u_obstacleRad.value = (obstacleRad * width) / actualWidth;

          // var velocity = new Uint16Array(width*height*4);
          // for (var i=0;i<height;i++){
          //     for (var j=0;j<width;j++){
          //         var index = 4*(i*width+j);
          //         velocity[index] = toHalf(1);
          //     }
          // }
          /*GPU.initTextureFromData("velocity", width, height, "HALF_FLOAT", null, true);//velocity
                GPU.initFrameBufferForTexture("velocity", true);
                GPU.initTextureFromData("nextVelocity", width, height, "HALF_FLOAT", null, true);//velocity
                GPU.initFrameBufferForTexture("nextVelocity", true);*/

          fb_resetVelocity.texture = textures.velocity;
          meshVisualizer.updateFrameBufferTexture(map.scene.frameState, fb_resetVelocity, viewport);
          fb_resetVelocity.texture = textures.nextVelocity;
          meshVisualizer.updateFrameBufferTexture(map.scene.frameState, fb_resetVelocity, viewport);

          /*GPU.initTextureFromData("velocityDivergence", width, height, "HALF_FLOAT", new Uint16Array(width * height * 4), true);
                GPU.initFrameBufferForTexture("velocityDivergence", true);
                GPU.initTextureFromData("pressure", width, height, "HALF_FLOAT", new Uint16Array(width * height * 4), true);
                GPU.initFrameBufferForTexture("pressure", true);
                GPU.initTextureFromData("nextPressure", width, height, "HALF_FLOAT", new Uint16Array(width * height * 4), true);
                GPU.initFrameBufferForTexture("nextPressure", true);*/

          // var numCols = Math.floor(actualHeight/10);
          // if (numCols%2 == 1) numCols--;
          // var numPx = actualHeight/numCols;

          // var material = new Uint16Array(actualWidth*actualHeight*4);
          // for (var i=0;i<actualHeight;i++){
          //     for (var j=0;j<actualWidth;j++){
          //         var index = 4*(i*actualWidth+j);
          //         if (j==0 && Math.floor((i-2)/numPx)%2==0) material[index] = toHalf(1.0);
          //     }
          // }
          /* GPU.initTextureFromData("material", actualWidth, actualHeight, "HALF_FLOAT", null, true);//material
                 GPU.initFrameBufferForTexture("material", true);
                 GPU.initTextureFromData("nextMaterial", actualWidth, actualHeight, "HALF_FLOAT", null, true);//material
                 GPU.initFrameBufferForTexture("nextMaterial", true);*/

          paused = false;
        }

        function onMouseMove(e) {
          lastMouseCoordinates = mouseCoordinates;
          mouseCoordinates = [e.clientX, actualHeight - e.clientY];
          updateObstaclePosition();
        }
        function onTouchMove(e) {
          e.preventDefault();
          var touch = e.touches[0];
          lastMouseCoordinates = mouseCoordinates;
          mouseCoordinates = [touch.pageX, actualHeight - touch.pageY];
          updateObstaclePosition();
        }

        function updateObstaclePosition() {
          if (movingObstacle) {
            obstaclePosition = mouseCoordinates;
          }
        }

        function onMouseDown() {
          var distToObstacle = [mouseCoordinates[0] - obstaclePosition[0], mouseCoordinates[1] - obstaclePosition[1]];
          if (distToObstacle[0] * distToObstacle[0] + distToObstacle[1] * distToObstacle[1] < obstacleRad * obstacleRad) {
            movingObstacle = true;
            mouseEnable = false;
          } else {
            mouseEnable = true;
            movingObstacle = false;
          }
        }

        function onMouseUp() {
          movingObstacle = false;
          mouseEnable = false;
        }
        var canvas = map.scene.canvas;
        //canvas.onmousemove = onMouseMove;
        //canvas.ontouchmove = onTouchMove;
        //canvas.onmousedown = onMouseDown;
        //canvas.ontouchstart = onMouseDown;
        //canvas.onmouseup = onMouseUp;
        //canvas.ontouchend = onMouseUp;
        //canvas.onmouseout = onMouseUp;
        //canvas.ontouchcancel = onMouseUp;

        var scene = map.scene;
        var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
        var lastMesh = null;
        var clickRequest = false;
        var mouseCoords = new Cesium.Cartesian2();
        var ray = new Cesium.Ray();
        function coords3dTo2d(cartesian3, cartesian2) {
          var xPercent = (cartesian3.x - west) / (east - west);
          var yPercent = (cartesian3.y - south) / (north - south);
          if (!cartesian2) {
            cartesian2 = new Cesium.Cartesian2();
          }
          cartesian2.x = xPercent * actualWidth;
          cartesian2.y = yPercent * actualHeight;
          return cartesian2;
        }
        handler.setInputAction(function (movement) {
          meshVisualizer.getPickRay(movement.position, ray);
          if (ray) {
            coords3dTo2d(ray.origin, mouseCoords);
            onMouseDown({ clientX: mouseCoords.x, clientY: mouseCoords.y });
            if (mouseCoords.x >= 0 && mouseCoords.x <= actualWidth && mouseCoords.y >= 0 && mouseCoords.y <= actualHeight) {
              scene.screenSpaceCameraController.enableInputs = false;
            } else {
              scene.screenSpaceCameraController.enableInputs = true;
            }
          } else {
            scene.screenSpaceCameraController.enableInputs = true;
          }
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
        handler.setInputAction(function (movement) {
          scene.screenSpaceCameraController.enableInputs = true;
        }, Cesium.ScreenSpaceEventType.RIGHT_DOWN);

        handler.setInputAction(function (movement) {
          meshVisualizer.getPickRay(movement.endPosition, ray);
          if (ray) {
            coords3dTo2d(ray.origin, mouseCoords);
            if (mouseCoords.x >= 0 && mouseCoords.x <= actualWidth && mouseCoords.y >= 0 && mouseCoords.y <= actualHeight) {
              onMouseMove({ clientX: mouseCoords.x, clientY: mouseCoords.y });
            }
          }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        handler.setInputAction(function (movement) {
          meshVisualizer.getPickRay(movement.endPosition || movement.position, ray);
          if (ray) {
            coords3dTo2d(ray.origin, mouseCoords);
            onMouseUp({ clientX: mouseCoords.x, clientY: mouseCoords.y });
            if (mouseCoords.x >= 0 && mouseCoords.x <= actualWidth && mouseCoords.y >= 0 && mouseCoords.y <= actualHeight) {
              scene.screenSpaceCameraController.enableInputs = false;
            } else {
              scene.screenSpaceCameraController.enableInputs = true;
            }
          } else {
            scene.screenSpaceCameraController.enableInputs = true;
          }
        }, Cesium.ScreenSpaceEventType.LEFT_UP);
      }
    </script>
  </body>
</html>
